package com.webchat.netty.gateway.handler;

import com.alibaba.nacos.api.naming.pojo.Instance;
import com.webchat.netty.gateway.router.RouterTable;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocket13FrameDecoder;
import io.netty.handler.codec.http.websocketx.WebSocket13FrameEncoder;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

@Slf4j
@Setter
public class NettyGatewayRouterHandler extends SimpleChannelInboundHandler<Object> {


    /**
     * 跟 netty server 链接管道
     */
    private volatile Channel backendChannel;

    private volatile Channel clientChannel;

    private volatile Promise<Channel> backendChannelPromise;

    private boolean isWebSocketUpgrade = false;

    private WebSocketClientHandshaker handshaker;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        this.clientChannel = ctx.channel();
        super.channelActive(ctx);
    }

    /**
     * 链接断开
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        log.info("Netty Chat WebSocket Connection Inactive！");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        log.info("Netty Chat WebSocket Connection Exp！", cause);
    }

    /**
     * gateway 不做WebSocket协议升级，长链接建立，最终我们ws链接由Netty server来完成
     * <p>
     * 这里制作请求转发
     *
     * @param ctx
     * @param msg
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) {

        // 识别请求是要建立链接还是链接已建立发数据包
        if (msg instanceof FullHttpRequest) {
            // 处理http请求
            // 需要gateway跟netty server建立链接
            // 要建立链接，创建一个netty客户端连接我们NettyServer，需要建立channel
            // 写数据（包括我们连接建立请求数据发送）
            doHandleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (isWebSocketUpgrade && msg instanceof WebSocketFrame) {
            // 处理ws数据包发送
            // 链接已经建立，发数据帧，复用当前chanel直接进行数据发送，发送到netty server
            doHandleWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }

    private void doHandleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame msg) {

        if (backendChannel != null && backendChannel.isActive()) {
            // 当前netty官网跟netty server之间的管道正常联通
            backendChannel.writeAndFlush(msg.retain());
        } else {
            log.error("Netty Gateway BackendChannel Connection Inactive!");
            // 管道断开这里不自动重连（重连机制主要浏览器客户端心跳检测、断开自动重连机制）
            ReferenceCountUtil.release(msg);
        }
    }

    private void doHandleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {

        // 判断是否要建立ws长链接，不是ws链接建立的http请求
        isWebSocketUpgrade = isWebSocketUpgrade(req);
        if (!isWebSocketUpgrade) {
            // 修复：非WebSocket请求立即释放
            req.release();
            return;
        }

        // 关键修改：增加引用计数防止提前释放
        req.retain();

        // 保证单一线程创建链接
        if (backendChannelPromise == null) {
            // 当前网关设计是单节点，不考虑分布式网关设计
            synchronized (this) {
                if (backendChannelPromise == null) {
                    backendChannelPromise = ctx.executor().newPromise();
                    // 初始化跟netty server 链接
                    initBackendConnection(ctx, req);
                }
            }
        }

        backendChannelPromise.addListener((Future<Channel> future) -> {
            if (future.isSuccess()) {
                // 转发请求到netty server
                doWriteToNettyServer(ctx, req);
            } else {
                // 确保释放请求
                ReferenceCountUtil.safeRelease(req);
                // 关闭资源
                closeChannel(ctx);
            }
        });
    }

    private void initBackendConnection(ChannelHandlerContext ctx, FullHttpRequest req) {

        // 要转发的目标netty server 节点（默认轮训策略）
        Instance instance = RouterTable.rt();
        //  构建目标URI (重要!)
        String targetUri = "ws://" + instance.getIp() + ":" + instance.getPort() + req.uri();
        handshaker = WebSocketClientHandshakerFactory.newHandshaker(
                URI.create(targetUri),
                WebSocketVersion.V13,
                null,
                false,
                req.headers());

        Bootstrap bootstrap = new Bootstrap();
        bootstrap.channel(NioSocketChannel.class)
                .group(ctx.channel().eventLoop())
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        // 设置http请求编解码处理器，使用默认的
                        pipeline.addLast(new HttpClientCodec());
                        // 合并http请求，设置允许请求数据大小，默认64kb
                        pipeline.addLast(new HttpObjectAggregator(65536 * 5));
                        // 关键添加：WebSocket客户端握手器
                        // 自定义处理器
                        pipeline.addLast(new BackendHandler(clientChannel, handshaker, req, NettyGatewayRouterHandler.this));
                    }
                });


        ChannelFuture channelFuture = bootstrap.connect(instance.getIp(), instance.getPort());
        channelFuture.addListener((ChannelFuture cf) -> {

            if (cf.isSuccess()) {
                // 链接建立成功
                backendChannel = cf.channel();
                backendChannelPromise.setSuccess(backendChannel);
            } else {
                // 链接建立失败
                backendChannelPromise.setFailure(cf.cause());
                // 关闭资源
                closeChannel(ctx);
            }
        });
    }


    /**
     * 转发请求到Netty Server
     *
     * @param req
     */
    private void doWriteToNettyServer(ChannelHandlerContext ctx, FullHttpRequest req) {

        // 执行握手请求
        handshaker.handshake(backendChannel)
                .addListener(future -> {
                    if (!future.isSuccess()) {
                        log.error("Handshake with backend failed", future.cause());
                        ReferenceCountUtil.safeRelease(req);
                        closeChannel(ctx);
                    }
                });
    }

    /**
     * 判断当前http请求是否ws请求
     *
     * @param req
     * @return
     */
    private boolean isWebSocketUpgrade(FullHttpRequest req) {

        return "websocket".equals(req.headers().get("upgrade"));
    }

    private static class BackendHandler extends ChannelInboundHandlerAdapter {

        private Channel frontChannel;
        private NettyGatewayRouterHandler nettyGatewayRouterHandler;
        private WebSocketClientHandshaker handshaker;
        private FullHttpRequest req;
        private static final String WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

        public BackendHandler(Channel frontChannel, WebSocketClientHandshaker handshaker, FullHttpRequest req,
                              NettyGatewayRouterHandler nettyGatewayRouterHandler) {
            this.frontChannel = frontChannel;
            this.handshaker = handshaker;
            this.req = req;
            this.nettyGatewayRouterHandler = nettyGatewayRouterHandler;
        }

        public static String calculateAccept(String secWebSocketKey) throws NoSuchAlgorithmException {
            // 拼接key和固定GUID
            String combined = secWebSocketKey + WEBSOCKET_GUID;

            // 计算SHA-1哈希
            MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
            byte[] sha1Hash = sha1.digest(combined.getBytes(StandardCharsets.UTF_8));

            // 进行Base64编码
            return Base64.getEncoder().encodeToString(sha1Hash);
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

            if (!handshaker.isHandshakeComplete()) {
                // 判断response是不是http response
                if (msg instanceof HttpResponse) {
                    // 判断response code 是否 101
                    FullHttpResponse response = (FullHttpResponse) msg;
                    // 完成握手过程
                    ChannelFuture channelFuture = handshaker.processHandshake(ctx.channel(), response);
                    channelFuture.addListener((ChannelFuture cf) -> {
                        if (cf.isSuccess()) {
                            log.info("WebSocket handshake completed with backend");
                            // 先写回101响应（此时管道仍有HTTP处理器）
                            // 先写回响应，然后再修改管道
                            log.info(response.toString());
                            response.headers().set("sec-websocket-accept", calculateAccept(req.headers().get("sec-websocket-key")));
                            frontChannel.writeAndFlush(response.retain()).addListener(f -> {
                                if (f.isSuccess()) {
                                    /**
                                     * 握手成功后处理
                                     */
                                    // 1. 处理clientChannel处理器
                                    // 处理浏览器到netty网关netty server管道handler
                                    ChannelPipeline clientPipeline = frontChannel.pipeline();
                                    // 删除http编解码（原因：当前ws已经完成握手，接下来我们需要处理数据帧，需要删除http编解码器，同时需要添加websocket的编解码器）
                                    clientPipeline.remove(HttpServerCodec.class);
                                    clientPipeline.remove(HttpObjectAggregator.class);
                                    // 需要注意我们这里需要头插
                                    // 修改前：HttpServerCodec -> HttpObjectAggregator -> NettyGatewayRouterHandler
                                    // 修改后：WebSocket13FrameDecoder -> WebSocket13FrameEncoder -> NettyGatewayRouterHandler
                                    clientPipeline.addFirst(new WebSocket13FrameDecoder(true, true, 65536 * 5),
                                                            new WebSocket13FrameEncoder(false));

                                    // 2. 处理backendChannel处理器
                                    Channel backendChannel = nettyGatewayRouterHandler.backendChannel;
                                    ChannelPipeline backendPipeline = backendChannel.pipeline();
                                    backendPipeline.addFirst(new WebSocket13FrameDecoder(false, true, 65536 * 5),
                                                             new WebSocket13FrameEncoder(true),
                                                             new WebSocketFrameForwarderHandler(frontChannel));

                                    log.info("WebSocket handshake completed and response sent to client");
                                }
                            });
                        } else {
                            log.error("WebSocket handshake failed with backend");
                            frontChannel.writeAndFlush(
                                    new DefaultFullHttpResponse(
                                            HttpVersion.HTTP_1_1,
                                            HttpResponseStatus.INTERNAL_SERVER_ERROR
                                    )
                            );
                        }
                    });
                }
            }
        }
    }

    private static class WebSocketFrameForwarderHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

        private final Channel channel;

        WebSocketFrameForwarderHandler(Channel channel) {
            this.channel = channel;
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) {
            // 将前端帧转发到后端服务器
            if (channel != null && channel.isActive()) {
                channel.writeAndFlush(msg.retain());
            } else {
//                ReferenceCountUtil.release(msg);
                log.warn("Backend channel not active, dropping frame");
            }
        }
    }

    private void closeChannel(ChannelHandlerContext ctx) {
        if (backendChannel != null) {
            backendChannel.close();
            backendChannel = null;
        }
        if (ctx != null) {
            ctx.close();
        }
        backendChannelPromise = null;
        isWebSocketUpgrade = true;
    }
}